package sagittarius.airfoil;

/**
 *
 */
public enum Geometry
    implements fv3.math.Notation
{
    /**
     * Conventional Model Geometry
     */
    TB2(PathOrder.TE_B2T,VertexDimension.XY, Axis.X,Axis.Y),
    /**
     * Enumerate of possible geometries
     */
    LB2(PathOrder.LE_B2T,VertexDimension.XY, Axis.X,Axis.Y),
    TB3(PathOrder.TE_B2T,VertexDimension.XYZ,Axis.X,Axis.Z),
    LB3(PathOrder.LE_B2T,VertexDimension.XYZ,Axis.X,Axis.Z),
    TT2(PathOrder.TE_T2B,VertexDimension.XY, Axis.X,Axis.Y),
    LT2(PathOrder.LE_T2B,VertexDimension.XY, Axis.X,Axis.Y),
    TT3(PathOrder.TE_T2B,VertexDimension.XYZ,Axis.X,Axis.Z),
    /**
     * Objective Section Geometry
     */
    LT3(PathOrder.LE_T2B,VertexDimension.XYZ,Axis.X,Axis.Z);


    public final static int Count = Geometry.values().length;

    public final static Geometry Model = Geometry.TB2;
    public final static Geometry Section = Geometry.LT3;


    /**
     *
     */
    public abstract static class Interpolation {
        /**
         *
         */
        public final static class Interpolation2
            extends Interpolation
        {
            public final float domain, range;


            protected Interpolation2(Geometry geometry, float d, float r){
                super(geometry);
                this.domain = d;
                this.range = r;
            }


            public boolean is2(){
                return true;
            }
            public float[] toArray(){

                final float[] vertices = new float[2];

                final int xD = this.geometry.domain();
                final int xR = this.geometry.range();

                vertices[xD] = this.domain;
                vertices[xR] = this.range;

                return vertices;
            }
        }
        /**
         *
         */
        public final static class Interpolation3
            extends Interpolation
        {
            public final float domain, range, independent;


            protected Interpolation3(Geometry geometry, float d, float r, float i){
                super(geometry);
                this.domain = d;
                this.range = r;
                this.independent = i;
            }


            public boolean is3(){
                return true;
            }
            public float[] toArray(){

                final float[] vertices = new float[3];

                final int xD = this.domain();
                final int xR = this.range();
                final int xI = this.independent();

                vertices[xD] = this.domain;
                vertices[xR] = this.range;
                vertices[xI] = this.independent;

                return vertices;
            }
        }


        public final Geometry geometry;


        protected Interpolation(Geometry geometry){
            super();
            this.geometry = geometry;
        }


        public abstract float[] toArray();

        public boolean is2(){
            return false;
        }
        public boolean is3(){
            return false;
        }
        public final int domain(){
            return this.geometry.domain();
        }
        public final int range(){
            return this.geometry.range();
        }
        public final int independent(){
            return this.geometry.independent();
        }
    }
    public enum GeomOrder {
        CW, CCW;
    }
    public enum EdgeOrder {
        LE, TE;
    }
    public enum SurfaceOrder {
        Top, Bottom;


        public SurfaceOrder next(){
            switch(this){
            case Top:
                return Bottom;
            case Bottom:
                return Top;
            default:
                throw new Error(this.name());
            }
        }
    }
    public enum PathOrder {
        /**
         * Model coordinates between zero and one, ordered from the
         * trailing edge at (1,0) to the leading edge at (0,0) -- in
         * clockwise order bottom then top.
         */
        TE_B2T(EdgeOrder.TE,SurfaceOrder.Bottom,GeomOrder.CCW),
        /**
         * Model coordinates between zero and one, ordered from the
         * leading edge at (1,0) to the trailing edge at (0,0) -- in
         * clockwise order bottom then top.
         */
        LE_B2T(EdgeOrder.LE,SurfaceOrder.Bottom,GeomOrder.CW),
        /**
         */
        TE_T2B(EdgeOrder.TE,SurfaceOrder.Top,GeomOrder.CW),
        /**
         * Section coordinates between zero and one, ordered from the
         * leading edge at (1,0) to the trailing edge at (0,0) -- in
         * counter-clockwise order top then bottom.
         */
        LE_T2B(EdgeOrder.LE,SurfaceOrder.Top,GeomOrder.CCW);


        public final EdgeOrder edge;

        public final SurfaceOrder surface;

        public final GeomOrder order;


        private PathOrder(EdgeOrder e, SurfaceOrder s, GeomOrder g){
            this.edge = e;
            this.surface = s;
            this.order = g;
        }
    }
    public enum Axis {
        X, Y, Z;


        public final static Axis Independent(Axis domain, Axis range){
            switch (domain){
            case X:
                switch (range){
                case Y:
                    return Z;
                case Z:
                    return Y;
                default:
                    throw new Error();
                }
            case Y:
                switch (range){
                case X:
                    return Z;
                case Z:
                    return Z;
                default:
                    throw new Error();
                }
            case Z:
                switch (range){
                case X:
                    return Y;
                case Y:
                    return X;
                default:
                    throw new Error();
                }
            default:
                throw new Error();
            }
        }
    }
    public enum VertexDimension {
        XY(2), XYZ(3);


        public final int number;


        private VertexDimension(int number){
            this.number = number;
        }
    }



    public final PathOrder path;

    public final VertexDimension dimension;

    public final Axis domain;

    public final Axis range;

    public final Axis independent;


    private Geometry(PathOrder p, VertexDimension d, 
                     Axis dd, Axis rr)
    {
        this.path = p;
        this.dimension = d;
        this.domain = dd;
        this.range = rr;
        this.independent = Axis.Independent(dd,rr);
    }


    public boolean is(PathOrder p){
        return (p == this.path);
    }
    public boolean is(VertexDimension d){
        return (d == this.dimension);
    }
    public boolean is(GeomOrder g){
        return (g == this.path.order);
    }
    public int domain(){
        return this.offset(this.domain);
    }
    public int range(){
        return this.offset(this.range);
    }
    public int independent(){
        return this.offset(this.independent);
    }
    public int offset(Axis a){
        /*
         * Switching on Axis to return fv3.math.Notation
         */
        switch(this.dimension){
        case XY:
            switch(a){
            case X:
                return X;
            case Y:
                return Y;
            case Z:
                return X;
            default:
                throw new Error(a.name());
            }
        case XYZ:
            switch(a){
            case X:
                return X;
            case Y:
                return Y;
            case Z:
                return Z;
            default:
                throw new Error(a.name());
            }
        default:
            throw new Error(a.name());
        }
    }
    public Interpolation interpolate(ChordVertex p, ChordVertex n, float d, SurfaceOrder s){
        final int DIM = this.dimension.number;
        final int DOM = this.domain();
        final int RAN = this.range();
        final int IND = this.independent();

        if (n.edge){

            final float[] nE = n.getEdge();

            final float[] pP = p.getPair();

            if (3 == DIM){

                float nDom = nE[DOM], pDom;
                float nRan = nE[RAN], pRan;
                float nInd = nE[IND], pInd;

                if (s == this.path.surface){
                    pDom = pP[DOM];
                    pRan = pP[RAN];
                    pInd = pP[IND];
                }
                else {
                    pDom = pP[DOM+DIM];
                    pRan = pP[RAN+DIM];
                    pInd = pP[IND+DIM];
                }

                final float r = MathAbstract.LIY(pDom,pRan,nDom,nRan,d);

                final float i = MathAbstract.LIY(pDom,pInd,nDom,nInd,d);

                return new Interpolation.Interpolation3(this,d,r,i);
            }
            else {

                float nDom = nE[DOM], pDom;
                float nRan = nE[RAN], pRan;

                if (s == this.path.surface){
                    pDom = pP[DOM];
                    pRan = pP[RAN];
                }
                else {
                    pDom = pP[DOM+DIM];
                    pRan = pP[RAN+DIM];
                }

                final float r = MathAbstract.LIY(pDom,pRan,nDom,nRan,d);

                return new Interpolation.Interpolation2(this,d,r);
            }
        }
        else if (p.edge){
            final float[] nP = n.getPair();

            final float[] pE = p.getEdge();

            if (3 == DIM){

                final float pDom = pE[DOM];
                final float pRan = pE[RAN];
                final float pInd = pE[IND];

                final float nDom = nP[DOM];
                final float nRan = nP[RAN];
                final float nInd = nP[IND];


                final float r = MathAbstract.LIY(pDom,pRan,nDom,nRan,d);

                final float i = MathAbstract.LIY(pDom,pInd,nDom,nInd,d);

                return new Interpolation.Interpolation3(this,d,r,i);
            }
            else {
                final float pDom = pE[DOM];
                final float pRan = pE[RAN];

                final float nDom = nP[DOM];
                final float nRan = nP[RAN];

                final float r = MathAbstract.LIY(pDom,pRan,nDom,nRan,d);

                return new Interpolation.Interpolation2(this,d,r);
            }
        }
        else {
            final float[] nP = n.getPair();

            final float[] pP = p.getPair();

            if (3 == DIM){

                final float nDom = nP[DOM];
                final float nRan = nP[RAN];
                final float nInd = nP[IND];

                final float pDom = pP[DOM];
                final float pRan = pP[RAN];
                final float pInd = pP[IND];


                final float r = MathAbstract.LIY(pDom,pRan,nDom,nRan,d);

                final float i = MathAbstract.LIY(pDom,pInd,nDom,nInd,d);

                return new Interpolation.Interpolation3(this,d,r,i);
            }
            else {

                final float nDom = nP[DOM];
                final float nRan = nP[RAN];

                final float pDom = pP[DOM];
                final float pRan = pP[RAN];


                final float r = MathAbstract.LIY(pDom,pRan,nDom,nRan,d);

                return new Interpolation.Interpolation2(this,d,r);
            }
        }
    }
    public float[] toArray(Interpolation a, 
                           Interpolation b)
    {
        final int n = this.dimension.number;

        float[] vertices = new float[n<<1];

        System.arraycopy(a.toArray(),0,vertices,0,n);

        System.arraycopy(b.toArray(),0,vertices,n,n);

        return vertices;
    }
}
